#include "myjql.h"

#include "buffer_pool.h"
#include "b_tree.h"
#include "table.h"
#include "str.h"

typedef struct {
    RID key;
    RID value;
} Record;

void read_record(Table *table, RID rid, Record *record) {
    table_read(table, rid, (ItemPtr)record);
}

RID write_record(Table *table, const Record *record) {
    return table_insert(table, (ItemPtr)record, sizeof(Record));
}

void delete_record(Table *table, RID rid) {
    table_delete(table, rid);
}

BufferPool bp_idx;
Table tbl_rec;
Table tbl_str;

void myjql_init() {
    b_tree_init("rec.idx", &bp_idx);
    table_init(&tbl_rec, "rec.data", "rec.fsm");
    table_init(&tbl_str, "str.data", "str.fsm");
}

void myjql_close() {
    /* validate_buffer_pool(&bp_idx);
    validate_buffer_pool(&tbl_rec.data_pool);
    validate_buffer_pool(&tbl_rec.fsm_pool);
    validate_buffer_pool(&tbl_str.data_pool);
    validate_buffer_pool(&tbl_str.fsm_pool); */
    b_tree_close(&bp_idx);
    table_close(&tbl_rec);
    table_close(&tbl_str);
}

int row_row_cmp(RID a, RID b) {
    Record kvp_a, kvp_b;
    read_record(&tbl_rec, a, &kvp_a);
    read_record(&tbl_rec, b, &kvp_b);

    StringRecord str_rec_a, str_rec_b;
    read_string(&tbl_str, kvp_a.key, &str_rec_a);
    read_string(&tbl_str, kvp_b.key, &str_rec_b);

    return compare_string_record(&tbl_str, &str_rec_a, &str_rec_b);
}

int ptr_row_cmp(void *p, size_t size, RID b) {
    Record kvp_b;
    read_record(&tbl_rec, b, &kvp_b);
    StringRecord str_rec_b;
    read_string(&tbl_str, kvp_b.key, &str_rec_b);

    char *str_a = (char*)p;

    int i = 0;
    for(i = 0; i < size; i++){
        char cha = str_a[i];
        char chb = next_char(&tbl_str, &str_rec_b);
        if(chb == '\0' || cha > chb) return 1;
        else if(cha < chb) return -1;
    }
    if(next_char(&tbl_str, &str_rec_b) == '\0') return 0;
    else return -1;
}

RID insert_handler(RID rid) {
    Record kvp;
    read_record(&tbl_rec, rid, &kvp);
    StringRecord str_rec;
    read_string(&tbl_str, kvp.key, &str_rec);
    char str[9999];
    size_t len = load_string(&tbl_str, &str_rec, str, 9999);

    RID new_rid = write_string(&tbl_str, str, len);
    Record new_kvp;
    new_kvp.key = new_rid;
    get_rid_block_addr(new_kvp.value) = -1;
    get_rid_idx(new_kvp.value) = 0;
    return write_record(&tbl_rec, &new_kvp);
}

void delete_handler(RID rid) {
    Record kvp;
    read_record(&tbl_rec, rid, &kvp);
    delete_string(&tbl_str, kvp.key);
    if(get_rid_block_addr(kvp.value) != -1)
        delete_string(&tbl_str, kvp.value);
    delete_record(&tbl_rec, rid);
}

size_t myjql_get(const char *key, size_t key_len, char *value, size_t max_size) {
    RID kvp_rid = b_tree_search(&bp_idx, key, key_len, &ptr_row_cmp);
    if(get_rid_block_addr(kvp_rid) == -1) return -1; // not exist
    Record kvp;
    read_record(&tbl_rec, kvp_rid, &kvp);
    StringRecord str_rec;
    read_string(&tbl_str, kvp.value, &str_rec);
    return load_string(&tbl_str, &str_rec, value, max_size);
}

void myjql_set(const char *key, size_t key_len, const char *value, size_t value_len) {
    myjql_del(key, key_len);
    Record kvp;
    kvp.key = write_string(&tbl_str, key, key_len);
    kvp.value = write_string(&tbl_str, value, value_len);
    RID kvp_rid = write_record(&tbl_rec, &kvp);
    b_tree_insert(&bp_idx, kvp_rid, &row_row_cmp, &insert_handler);
}

void myjql_del(const char *key, size_t key_len) {
    RID kvp_rid = b_tree_search(&bp_idx, key, key_len, &ptr_row_cmp);
    if(get_rid_block_addr(kvp_rid) == -1) return; // not exist
    Record kvp;
    read_record(&tbl_rec, kvp_rid, &kvp);
    b_tree_delete(&bp_idx, kvp_rid, &row_row_cmp, &insert_handler, &delete_handler);
    delete_string(&tbl_str, kvp.key);
    delete_string(&tbl_str, kvp.value);
    delete_record(&tbl_rec, kvp_rid);
}

/* void myjql_analyze() {
    printf("Record Table:\n");
    analyze_table(&tbl_rec);
    printf("String Table:\n");
    analyze_table(&tbl_str);
} */